package java_thinking.unit_15._15_9;


import java.util.ArrayList;
import java.util.List;

/**
 * @Description :   逆变的演变
 * @author: cww
 * @DateTime: 2019-04-06 11:12
 */
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class SuperTypeWildcards {
    /**
     * 超类型通配符<? super MyClass> 或者 <? super T>
     *     MyClass是下界的意思
     * @param apples
     */
    static void writeTo(List<? super Apple> apples) {
        //这样添加就安全了
        apples.add(new Apple());
        apples.add(new Jonathan());
        //! apples.add(new Fruit());
    }
}

/**
 * ****************************************************************************
 *
 * 什么是协变返回类型    一定要看 https://blog.csdn.net/li2327234939/article/details/50786860?utm_source=blogkpcl1
 * 在面向对象程序设计中，协变返回类型指的是子类中的成员函数的返回值类型不必严格等同于父
 * 类中被重写的成员函数的返回值类型，而可以是更 "狭窄" 的类型。
 *
 * Java 5.0添加了对协变返回类型的支持，即子类覆盖（即重写）基类方法时，返回的类型可以是
 * 基类方法返回类型的子类。协变返回类型允许返回更为具体的类型。
 *
 *
 * extends确定了泛型的上界，而super确定了泛型的下界
 */
class GenericWriting {
    static <T> void writeExact(List<T> list,T item) {
        list.add(item);
    }
    static List<Apple> apples = new ArrayList<>();
    static List<Fruit> fruits = new ArrayList<>();
    static void f1() {
        writeExact(apples,new Apple());
        //这里会报错，因为容器的类型不同(注意这不是持有类型)
        //! writeExact(fruits,new Apple());
    }

    /**
     *  现在这里的参数是List<? super T>，容器*持有*从T导出的某种具体类型，
     *  这样就可以安全的将一个T类型的对象或者从T类型导出的任何对象作为参数传递给List方法
     *  此时我们就可以将Apple放到Fruit中了
     */
    static <T> void writeWithWildcard(List<? super T> list,T item) {
        list.add(item);
    }
    static void f2() {
        writeWithWildcard(apples,new Apple());
        //此时我们就可以将Apple放到Fruit中了
        writeWithWildcard(fruits,new Apple());
    }
}
